package com.ruoyi.framework.security.password;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Md5PasswordEncoder
 */
public class Md5PasswordEncoder implements PasswordEncoder {

    public static final ThreadLocal<String> LOCAL_USER_NAME = new ThreadLocal<>();
    /**
     * 盐值字节数组长度，经过Hex编码后为长度6的十六进制字符串
     */
    private static final int SALT_BYTE_LENGTH = 3;
    private static Log log = LogFactory.getLog(Md5PasswordEncoder.class);
    private final BytesKeyGenerator saltGenerator;
    private final MessageDigest md5;

    public Md5PasswordEncoder() {
        this.saltGenerator = KeyGenerators.secureRandom(SALT_BYTE_LENGTH);
        try {
            this.md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            log.error("创建MD5摘要工具失败", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public String encode(CharSequence rawPassword) {
        try {
            String userName = LOCAL_USER_NAME.get();
            return encode(userName, rawPassword);
        } finally {
            LOCAL_USER_NAME.remove();
        }
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        try {
            String userName = LOCAL_USER_NAME.get();
            String hexSalt = encodedPassword.substring(0, 6);
            String digestedHexPassword = encodedPassword.substring(6);
            byte[] digested = decode(digestedHexPassword);
            return MessageDigest.isEqual(digested, decode(digest(userName, rawPassword)));
        } finally {
            LOCAL_USER_NAME.remove();
        }
    }

    /**
     * 对密码进行加密操作
     *
     * @param userName    登录用户名
     * @param rawPassword 明文密码
     * @return 加密密码（Hex编码）
     */
    private String encode(String userName, CharSequence rawPassword) {
        return digest(userName, rawPassword);
    }

    private String digest(String userName, CharSequence rawPassword) {
        // 加盐后的密码，用户名在前，明文密码在中，盐值在后
        byte[] saltedPassword = Utf8.encode((userName + rawPassword));
        // 摘要后的密码
        byte[] digestedPassword = md5.digest(saltedPassword);
        // 经过Hex编码处理的密文密码
        char[] hexEncodedPassword = Hex.encode(digestedPassword);
        // 密文密码字符串
        return new String(hexEncodedPassword);
    }

    /**
     * 解码密文密码
     *
     * @param encodedPassword 密文密码
     * @return 经过Hex解码的二进制密文密码
     */
    private byte[] decode(CharSequence encodedPassword) {
        return Hex.decode(encodedPassword);
    }

}
